<template>
<el-dialog title="上传素材" :visible.sync="editVisible" width="50%">
    <div id="global-uploader">
      <uploader
        ref="uploader"
        :options="initOptions"
        :fileStatusText="fileStatusText"
        :autoStart="false"
        @file-added="onFileAdded"
        @file-success="onFileSuccess"
        @file-progress="onFileProgress"
        @file-error="onFileError"
        class="uploader-app"
      >
        <uploader-unsupport></uploader-unsupport>
        <uploader-btn id="global-uploader-btn" ref="uploadBtn">
          <el-button type="primary" @click.stop="openUpload">文件上传<i class="el-icon-upload el-icon--right"></i></el-button>
        </uploader-btn>
        <p style="padding:8px;color:red;font-size:14px;">(上传文件类型为:{{accept.join()}})</p>
        <p style="padding:8px;color:red;font-size:14px;">(上传期间请勿关闭浏览器或刷新页面)</p>
        <uploader-list v-show="panelShow">
          <div class="file-panel" slot-scope="props">
            <el-table
              :data="props.fileList"
              border
              size="mini"
              ref="multipleTable"
              header-cell-class-name="table-header"
            >
              <el-table-column label="资源名" align="center">
                <template slot-scope="scope">{{ scope.row.name }}</template>
              </el-table-column>
              <el-table-column label="文件类型" align="center">
                <template slot-scope="scope">{{ scope.row.fileType }}</template>
              </el-table-column>
              <el-table-column label="文件大小" align="center">
                <template slot-scope="scope">{{
                  fileSize(scope.row.size)
                }}</template>
              </el-table-column>
              <el-table-column label="操作" width="80" align="center">
                <template slot-scope="scope">
                  <el-button
                    type="text"
                    icon="el-icon-delete"
                    class="red"
                    @click="handleDelete(scope.row)"
                    >移除</el-button
                  >
                </template>
              </el-table-column>
            </el-table>
          </div>
        </uploader-list>
      </uploader>
    </div>
    <span slot="footer" class="dialog-footer">
      <el-button @click="editVisible = false">取 消</el-button>
      <el-button type="primary" @click="submit">立即上传</el-button>
    </span>
  </el-dialog>
</template>

<script>
/**
 *  全局上传插件，两种调用方式
 *   1. 作为全局页面的组件，使用event bus
 *   调用方法：Bus.$emit('openUploader', {params: {}, options: {}})
 *               params: 发送给服务器的额外参数；
 *               options：上传选项，目前支持 target、testChunks、mergeFn、accept
 *
 *   监听函数：Bus.$on('fileAdded', fn); 文件选择后的回调
 *           Bus.$on('fileSuccess', fn); 文件上传成功的回调，监听后记得释放
 *
 *   2. 作为普通组件在单个页面中调用，使用props
 */
import { ACCEPT_CONFIG } from "./js/config";
import Bus from "../bus";
import SparkMD5 from "spark-md5";
import { mergeSimpleUpload } from "@/api/index";
import { getfilesize } from "@/utils/utils";

export default {
  props: {
    global: {
      type: Boolean,
      default: true,
    },
    // 发送给服务器的额外参数
    params: {
      type: Object,
    },
    options: {
      type: Object,
    },
    globalFileName:{
      type: String,
      default: '',
    },
    originName:{
      type: String,
      default: '',
    }
  },

  data() {
    return {
      initOptions: {
         target: (this.$API.ASSET_SERVER_URL != '/'?this.$API.ASSET_SERVER_URL+'/index.php?s=':'/')+this.$API.MEDIA_FILE_UPLOADS,
        chunkSize: "2048000",
        fileParameterName: "file",
        maxChunkRetries: 3,
        testChunks: true,
        query: (file, chunk) => {
          return {
            ...file.params
          }
        },
        checkChunkUploadedByResponse:function(chunk,message){
          let objMessage=JSON.parse(message);
          if(objMessage.code == 1){
            return true;
          }
          return false;
        }
      },
      fileStatusText: {
        success: "上传成功",
        error: "上传失败",
        uploading: "上传中",
        paused: "已暂停",
        waiting: "等待上传",
      },
      panelShow: false, //选择文件后，展示上传panel
      collapse: false,
      customParams: {},
      customStatus: "",
      fileTotal: 0,
      fileType:1,
      accept:[],
      editVisible:false
    };
  },
  mounted() {
    Bus.$on('global-uploader-open', (isShow,param) => {
        this.editVisible = isShow;
        if(isShow){
          this.panelShow=false;
          Bus.$emit("global-close-open", false);
          Bus.$emit("global-uploader-list", []);
          setTimeout(()=>this.setUpLoadAttr(param),300)
        }
    });
  },

  computed: {
    // Uploader实例
    uploader() {
      return this.$refs.uploader && this.$refs.uploader.uploader;
    },
  },

  methods: {
    openUpload(){
      let input = document.querySelector("#global-uploader-btn input");
      input.click();
    },
    fileSize(size) {
      return getfilesize(size);
    },
    setUpLoadAttr(param){
      if(param){
        this.uploader.files = [];
        this.uploader.fileList = [];
        // 确定文件类型
        const input = document.querySelector("#global-uploader-btn input");
        this.accept = param.type == 0?ACCEPT_CONFIG.image:ACCEPT_CONFIG.video;
        input.setAttribute("accept", this.accept.join());
        this.customParams={
          media_id:param.media_id,
          media_file_id:param.media_file_id || '',
          type:param.type
        }
      }
    },
    handleDelete(file) {
      this.uploader.removeFile(file, true);
    },
    // 自定义options
    customizeOptions(opts) {
      // 自定义上传url
      if (opts.target) {
        this.uploader.opts.target = opts.target;
      }

      // 是否可以秒传、断点续传
      if (opts.testChunks !== undefined) {
        this.uploader.opts.testChunks = opts.testChunks;
      }

      // merge 的方法，类型为Function，返回Promise
      this.mergeFn = opts.mergeFn || mergeSimpleUpload;

      // 自定义文件上传类型
      let input = document.querySelector("#global-uploader-btn input");
      let accept = opts.accept || ACCEPT_CONFIG.getAll();
      input.setAttribute("accept", accept.join());
    },
    // 提交上传资源
    submit(){
      const list = this.uploader.fileList;
      if(list.length<=0){
        return this.$message.error('请选择一个资源');
      }
      this.editVisible=false;
      Bus.$emit("global-close-open", true);
      Bus.$emit("global-uploader-list", list);
      setTimeout(()=>{
        list.forEach(item=>{
          this.computeMD5(item).then((result) => this.startUpload(result))
        })
      },500)
    },
    onFileAdded(file) {
      console.log("file", file);
      this.panelShow = true;
      file.params = this.customParams;
      // this.emit('fileAdded')

      // 将额外的参数赋值到每个文件上，以不同文件使用不同params的需求
      // 

      // 计算MD5
      // this.computeMD5(file).then((result) => this.startUpload(result))
    },

    /**
     * 计算md5值，以实现断点续传及秒传
     * @param file
     * @returns Promise
     */
    computeMD5(file) {
      let fileReader = new FileReader();
      let time = new Date().getTime();
      let blobSlice =
        File.prototype.slice ||
        File.prototype.mozSlice ||
        File.prototype.webkitSlice;
      let currentChunk = 0;
      const chunkSize = 10 * 1024 * 1000;
      let chunks = Math.ceil(file.size / chunkSize);
      let spark = new SparkMD5.ArrayBuffer();
      let fileType={type:'md5',msg:'md5计算中..',progress:0,typeParmes:{}}
      // 文件状态设为"计算MD5"
      this.statusSet(file.id, fileType);
      file.pause();
      loadNext();
      return new Promise((resolve, reject) => {
        fileReader.onload = (e) => {
          spark.append(e.target.result);
          if (currentChunk < chunks) {
            currentChunk++;
            loadNext();
            // 实时展示MD5的计算进度
            fileType.progress=((currentChunk / chunks) * 100).toFixed(0);
            this.statusSet(file.id, fileType);
          } else {
            let md5 = spark.end();
            // md5计算完毕
            resolve({ md5, file });
            fileType={type:'md5',progress:100,msg:'md5计算完成',typeParmes:{md5,chunks,size:file.size,time:(new Date().getTime() - time)+'ms'}}
            this.statusSet(file.id, fileType);
            // console.log(
            //   `MD5计算完毕：${file.name} \nMD5：${md5} \n分片：${chunks} 大小:${
            //     file.size
            //   } 用时： ms`
            // );
          }
        };

        fileReader.onerror = function () {
          fileType.type='error';
          fileType.msg='读取出错';
          this.statusSet(file.id, fileType);
          // this.error(`文件${file.name}读取出错，请检查该文件`);
          file.cancel();
          reject();
        };
      });

      function loadNext() {
        let start = currentChunk * chunkSize;
        let end =
          start + chunkSize >= file.size ? file.size : start + chunkSize;

        fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
      }
    },

    // md5计算完毕，开始上传
    startUpload({ md5, file }) {
      file.uniqueIdentifier = md5;
      file.resume();
    },

    onFileSuccess(rootFile, file, response, chunk) {
      let res = JSON.parse(response),fileType={type:'transcoding',msg:'切片合并中',progress:100,typeParmes:{}};
      if(res.code == 1){
        if(res.merge === false){
          fileType.type='success';
          fileType.msg='上传成功';
          this.statusSet(file.id, fileType);
        }else{
          this.statusSet(file.id, fileType);
          this.$http.post(this.$API.MEDIA_FILE_MERGE,{
            media_id: file.params.media_id,
            identifier: file.uniqueIdentifier,
            filename: file.name,
            totalSize: file.size,
            totalChunks: chunk.offset + 1
          }).then(result=>{
            if(result.code == 1){
              fileType.type='success';
              fileType.msg='上传成功';
            }else{
              fileType.type='error';
              fileType.msg='切片合并失败';
            }
            this.statusSet(file.id, fileType);
          }).catch(err=>{
            fileType.type='error';
            fileType.msg='切片合并失败';
            this.statusSet(file.id, fileType);
          })
        }
      }else{
        fileType.type='error';
        fileType.msg='上传失败';
        fileType.progress=0;
        this.statusSet(file.id, fileType);
      }
    },

    onFileProgress(rootFile, file, chunk) {
      if(chunk && chunk.processedState && chunk.processedState.res){
        let chunkObj=null;
        try{
          chunkObj=JSON.parse(chunk.processedState.res)
        }catch(e){}
        if(chunkObj && chunkObj.code == 1){
          const progress=((chunk.endByte/file.size)*100).toFixed(0);
          let fileType={type:'start',msg:'上传中',progress:progress,currentSpeed:file.currentSpeed,typeParmes:{}}
          this.statusSet(file.id,fileType)
        }
      }
    },

    onFileError(rootFile, file, response, chunk) {
      const fileType={type:'error',msg:'上传失败',progress:0,typeParmes:{}};
      this.statusSet(file.id, fileType);
    },

    mergeFn(data){
      return this.$http.post(this.$API.a,data)
    },

    close() {
      this.uploader.cancel();

      this.panelShow = false;
    },
    statusSet(id,status){
      Bus.$emit("global-uploader-change", id,status);
    },
    oncomplete(){
      console.log('文件上传结束')
    },
    /**
     * 新增的自定义的状态: 'md5'、'merging'、'transcoding'、'failed'
     * @param id
     * @param status
     */
    statusSet111(id, status) {
      let statusMap = {
        md5: {
          text: "校验MD5",
          bgc: "#fff",
        },
        merging: {
          text: "合并中",
          bgc: "#e2eeff",
        },
        transcoding: {
          text: "转码中",
          bgc: "#e2eeff",
        },
        failed: {
          text: "上传失败",
          bgc: "#e2eeff",
        },
      };

      this.customStatus = status;
      this.$nextTick(() => {
        const statusTag = document.createElement("p");
        statusTag.className = `custom-status-${id} custom-status`;
        statusTag.innerText = statusMap[status].text;
        statusTag.style.backgroundColor = statusMap[status].bgc;

        const statusWrap = document.querySelector(
          `.file_${id} .uploader-file-status`
        );
        statusWrap.appendChild(statusTag);
      });
    },

    statusRemove(id) {
      this.customStatus = "";
      this.$nextTick(() => {
        const statusTag = document.querySelector(`.custom-status-${id}`);
        statusTag.remove();
      });
    },

    emit(e) {
      Bus.$emit(e);
      this.$emit(e);
    },

    error(msg) {
      this.$notify({
        title: "错误",
        message: msg,
        type: "error",
        duration: 2000,
      });
    },
  },
};
</script>

<style lang="less">
#global-uploader {
  // &:not(.global-uploader-single) {
  //   position: fixed;
  //   z-index: 20;
  //   right: 15px;
  //   bottom: 15px;
  //   box-sizing: border-box;
  // }

  .uploader-app {
    position: relative;
  }

  .file-panel {
    // background-color: #fff;
    // border: 1px solid #e2e2e2;
    // border-radius: 7px 7px 0 0;
    // box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);

    .file-title {
      display: flex;
      height: 40px;
      line-height: 40px;
      padding: 0 15px;
      border-bottom: 1px solid #ddd;

      .operate {
        flex: 1;
        text-align: right;

        i {
          font-size: 18px;
        }
      }
    }

    .file-list {
      position: relative;
      height: 240px;
      overflow-x: hidden;
      overflow-y: auto;
      background-color: #fff;
      transition: all 0.3s;

      .file-item {
        background-color: #fff;
      }
    }

    // &.collapse {
    //   .file-title {
    //     background-color: #e7ecf2;
    //   }
    //   .file-list {
    //     height: 0;
    //   }
    // }
  }

  .no-file {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 16px;
  }

  .uploader-file {
    // &.md5 {
    //   .uploader-file-resume {
    //     display: none;
    //   }
    // }
  }

  .uploader-file-icon {
    &:before {
      content: "" !important;
    }

    &[icon="image"] {
      // background: url(./images/image-icon.png);
    }
    &[icon="audio"] {
      // background: url(./images/audio-icon.png);
      background-size: contain;
    }
    &[icon="video"] {
      // background: url(./images/video-icon.png);
    }
    &[icon="document"] {
      // background: url(./images/text-icon.png);
    }
    &[icon="unknown"] {
      // background: url(./images/zip.png) no-repeat center;
      background-size: contain;
    }
  }

  .uploader-file-actions > span {
    margin-right: 6px;
  }

  .custom-status {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1;
  }
}

/* 隐藏上传按钮 */
#global-uploader-btn {
  // position: absolute;
  // clip: rect(0, 0, 0, 0);
  border:1px solid transparent;
}

.global-uploader-single {
  #global-uploader-btn {
    position: relative;
  }
}
</style>
